home *** CD-ROM | disk | FTP | other *** search
- #!/usr/local/bin/gawk -f
- #!/usr/bin/awk -f
- # @(#) libupdate.awk 1.0 96/11/04
- # 96/11/04 john h. dubois iii (john@armory.com)
-
- # Use of @(\#) is to prevent these strings from being recognized by programs
- # run on this text file
- BEGIN {
- Name = "libupdate"
- Usage = \
- "Usage: " Name \
- " [-hdSqBrpPNRxn] [-s<string>] [-b<begin-line>] [-e<end-line>]\n"\
- " [-E<ext>] [-V<version-string>] [-[cCM]<compare-libs>] newlib [file ...]"
- rcFile = ".libupdate"
- # Used: bBcCdefFEhmMNpPqrRsSuUvVWx
- ARGC = Opts(Name,Usage,"BE;s:SdrqRhxb;e;V;c:C;pPNM;u;U;",0,
- "~/" rcFile ":$UHOME/" rcFile,"NOBACKUP,EXTENSION,STARTSTRING,"\
- "NOSTARTSTRING,NOCHECK,REQVERSION,QUIET,REGEX",
- 0,"n",0,"",
- "s,S;d,Vr;c,C;p,VrCd;P,EVrp;N,cCEVr;M,cCpsSbeRVr;uU,cCMBEsSbeRdVrpPN;u,U")
- startString = "### "
- if ("h" in Options) {
- printf \
- "%s: Update libraries included in the text of interpreted programs.\n"\
- "%s\n"\
- " newlib is the filename of a library to substitute into each of the\n"\
- "other named files as a replacement of an older version of the library.\n"\
- "The substitution is done by getting the first and last lines of newlib,\n"\
- "finding the identical copies of those lines in the file being processed,\n"\
- "and replacing the lines and everything between them with contents of the\n"\
- "new library. The first and last lines must be different and there must be\n"\
- "only one instance of each in each processed file. Both the first and last\n"\
- "lines of the library are normally required to begin with the string\n"\
- "\"%s\" as a sanity check; this can be changed with the -s option.\n"\
- " If newlib contains an SCCS string of the form\n"\
- "\"@(\#) <libname> <version-#> <date><end-of-line>\", then each replaced\n"\
- "library will be searched for an SCCS string that begins with\n"\
- "\"@(\#) <libname>\". If such a string is found and the version number or\n"\
- "date is later than that in the replacement library, a warning is issued\n"\
- "and the file is not updated. This can be turned off with the -d option.\n"\
- "<date> should have the form year/month/day. <version-#> should contain a\n"\
- "sequence of numeric strings separated by '.' characters. Each pair of\n"\
- "numeric strings is compared (as integers) separately, with strings on the\n"\
- "left having higher value.\n"\
- " A copy of each file is put in filename- when the file is processed.\n"\
- "If no filenames are given, a single file is read from the standard input\n"\
- "and the modified version is written to the standard output.\n"\
- "Options:\n"\
- "Some of the following options can also be set by assigning values to\n"\
- "variables in a configuration file named %s. This file is searched for\n"\
- "first in the home directory of the invoking user and then, if the\n"\
- "environment variable UHOME is set, in the directory specified by that\n"\
- "variable. If both files exist, variables assignments in the first file\n"\
- "override those in the second file. Variables are assigned to with the\n"\
- "syntax: varname=value or in the case of flags, by simply putting the\n"\
- "indicated variable name in the file without a value. To turn off a flag\n"\
- "option in the first file and prevent it from being set in the second file,\n"\
- "assign it a value of 0. e.g. if FOO is set in the first file, FOO=0 in\n"\
- "the second file will override it. Flag options can be turned off on the\n"\
- "command line by following them immediately with \"-\", e.g. -v- to turn\n"\
- "off the v option in such a way that it cannot be turned on in a config\n"\
- "file. Variable names appear in parentheses in the option descriptions.\n"\
- "-h: Print this help.\n"\
- "-c<compare-libs>: Compare the library found in each file line-for-line to\n"\
- " the the contents of the library files given in the colon- or\n"\
- " whitespace-separated list <compare-libs>. If the library in a file is\n"\
- " not exactly the same as that in one of the compare-libs, a complaint\n"\
- " is issued and no update is done.\n"\
- "-C<compare-libs>: Like -c, except that the only result of a lack of match\n"\
- " is the complaint; the update is still done.\n"\
- "-M<compare-libs>: Like -c, except that no checks for matching first or\n"\
- " last line are done. Instead, each file to be processed is searched\n"\
- " until a sequence of lines matching that in one of the given files is\n"\
- " found. That set of lines is then replaced with the new library. The\n"\
- " search done by this option may take a long time.\n"\
- "-B: Do not makes backup copies of processed files. (NOBACKUP)\n"\
- "-E<ext>: Instead of making backup copies, write updated files to the same\n"\
- " filename with <ext> appended. (EXTENSION)\n"\
- "-x: Turn on debugging.\n"\
- "-s<string>: Require the first and last lines of newlib to start with\n"\
- " <string>. (STARTSTRING)\n"\
- "-S: Do not require the first and last lines of newlib to start with any\n"\
- " special string; equivalent to -s \"\". (NOSTARTSTRING)\n"\
- "-b<begin-line>: Use <begin-line> as the starting line of the library to\n"\
- " search for in each file.\n"\
- "-e<end-line>: Use <end-line> as the ending line of the library to search\n"\
- " for in each file.\n"\
- "If multiple -b or -e options are given, each will be searched for.\n"\
- "-R: Treat the strings given with -b and -e as unanchored regular\n"\
- " expressions style of egrep rather than as fixed strings. (REGEX)\n"\
- "-d: Do not check library version numbers or dates. (NOCHECK)\n"\
- "-V<version-string>: Use <version-string> as the source of the new library\n"\
- " version number and date instead of reading it from the new library.\n"\
- " It should have three words: <libname> <version-#> <date>\n"\
- "-r: Require replaced libraries to have a version string for comparison.\n"\
- " This also requires the new library to have a version string, unless\n"\
- " -V is given. (REQVERSION)\n"\
- "-n: Do not read any configuration files.\n"\
- "-q: Quiet. Turns off some messages. (QUIET)\n"\
- "The following options cause %s to generate output based on the\n"\
- "contents of each file rather than updating them. If any is given with\n"\
- "both -b and -e, then no <newlib> should be given; i.e. the first filename\n"\
- "argument to %s should be the first file to be processed rather than a\n"\
- "library file. The same is true if -M is given with -P or -N.\n"\
- "-p: Print the library contained in a file. If -E is also given, the\n"\
- " library is written to a filename as described for the -E option. If\n"\
- " not, the library is written to the standard output; in this case, only\n"\
- " one source filename should be given. The -d option is always turned\n"\
- " on. If -c is given with -p, the meaning of the -c option is inverted:\n"\
- " output will be produced only if the library contained in a file is NOT\n"\
- " identical to any of the named libraries. Additionally, each library\n"\
- " read from a file will be added to the set of libraries for comparison,\n"\
- " so only one instance of each unique library will be output. An empty\n"\
- " string may be given to -c to turn on this behaviour without having to\n"\
- " name any already-existing libraries for comparison.\n"\
- "-P: Print the version string found in each file.\n"\
- "-N: Print the names of files that no library is found in.\n"\
- "-u<name>: Search the entirity of each file for an SCCS string with the\n"\
- " format described above and the name <name>. For the first such string\n"\
- " found in each file, a line with this format is printed:\n"\
- " filename version date line-number\n"\
- "-U<name>: Like -u, except that only the first instance of each version\n"\
- " found is printed.\n",
- Name,Usage,startString,rcFile,Name,Name
- exit 0
- }
- if ("U" in Options) {
- vName = Options["U"]
- uniqueVersion = 1
- }
- if ("u" in Options)
- vName = Options["u"]
- if (Debug = "x" in Options)
- printf "Debugging is on.\n"
- numFiles = ARGC - 1
- argNum = 1
- if ((regEx = "R" in Options) && Options["R","num",1] != 0 && \
- !("b" in Options || "e" in Options)) {
- # If -R given on command line w/o -b or -e...
- printf "%s: Must give -b or -e if -R is given. Exiting.\n",
- Name > "/dev/stderr"
- exit 1
- }
- matchLibs = "M" in Options
- printLib = "p" in Options
- printLibVersion = "P" in Options
- noLib = "N" in Options
- printOnly = printLib || printLibVersion || noLib
- if (!(printOnly && ("b" in Options && "e" in Options || "M" in Options) \
- || vName != "")) {
- if (!numFiles) {
- printf "%s: No library named. Use -h for help.\n",
- Name > "/dev/stderr"
- exit 1
- }
- libFile = ARGV[1]
- libLines = readLib(libFile,library)
-
- numFiles--
- argNum++
- }
- backup = !("B" in Options)
- versionRequired = "r" in Options
- quiet = "q" in Options || (!Debug && printOnly)
- if (compareWarning = ("C" in Options)) {
- libFiles = Options["C"]
- compareLibs = 1
- }
- else if ("c" in Options) {
- compareLibs = 1
- libFiles = Options["c"]
- if (printLib)
- addLib = 1
- }
- else if (matchLibs) {
- compareLibs = 1
- libFiles = Options["M"]
- }
- checkVersion = !(("d" in Options) || printOnly || compareLibs)
- if ("E" in Options) {
- if (!numFiles) {
- printf \
- "%s: Cannot use -E option when reading from standard input. Exiting.\n",
- Name > "/dev/stderr"
- exit 1
- }
- newExt = Options["E"]
- backup = 0
- }
- else if (printLib && numFiles > 1) {
- printf "%s: may only give one filename if -p is used without -E.\n",
- Name > "/dev/stderr"
- exit 1
- }
- if ("s" in Options)
- startString = Options["s"]
- else if ("S" in Options || "M" in Options)
- startString = ""
-
- if (!matchLibs && vName == "") {
- getLibMarker("first",startString,libFile,Options,"b",library[1],
- firstLines)
- getLibMarker("last",startString,libFile,Options,"e",library[libLines],
- lastLines)
- if (Intersection(firstLines,lastLines,Common)) {
- printf "%s: Got identical first and last lines (exiting):\n",
- Name > "/dev/stderr"
- for (line in Common)
- print line > "/dev/stderr"
- exit 1
- }
- }
-
- if (libLines) {
- if ("V" in Options) {
- if (!getVersion(Options["V"],versionInfo,1)) {
- printf "%s: Bad version string: %s\n",Name,
- Options["V"] > "/dev/stderr"
- exit 1
- }
- }
- else if (\
- !getLibVersion(library,1,0,versionInfo,quiet,libFile,versionRequired))
- checkVersion = 0
- }
-
- if (!numFiles) {
- numFiles++
- ARGV[argNum] = "/dev/stdin"
- }
- if (!matchLibs && Debug) {
- printf "%d library starting %s(s):\n%s\n"\
- " %d library ending %s(s):\n%s\n",NumElem(firstLines),
- regEx ? "pattern" : "line",conglom(firstLines,"\n"),NumElem(lastLines),
- regEx ? "pattern" : "line",conglom(lastLines,"\n") > "/dev/stderr"
- printf "%d file(s) to be processed.\n",numFiles > "/dev/stderr"
- }
- if (compareLibs)
- if (!readLibs(libFiles,firstLines,lastLines,libs,versionRequired,quiet,
- (name in versionInfo) ? versionInfo["name"] : "",regEx,"b" in Options,
- "e" in Options,matchLibs) && !(compareWarning || printLib)) {
- printf "%s: No libraries given.\n",Name > "/dev/stderr"
- exit 1
- }
- for (; numFiles; numFiles--)
- procFile(ARGV[argNum++],library,firstLines,lastLines,backup,
- Debug,checkVersion,quiet,versionInfo,newExt,versionRequired,printLib,
- printLibVersion,noLib,compareLibs,compareWarning,libs,regEx,matchLibs,
- addLib,vName,uniqueVersion)
- exit 0
- }
-
- function conglom(set,sep, s,l) {
- for (l in set)
- s = s sep l
- return substr(s,1+length(sep))
- }
-
- # Reads each file in libFiles into libs.
- # The lines of each file are placed in libs[filenum,1..numlines]
- # where filenum is an integer starting with 1.
- # The number of files is stored in libs["numfiles"].
- # The number of lines read from each file is stored in libs[filenum,"numlines"]
- # The number of chars read from each file is stored in libs[filenum,"numchars"]
- # The name of each file is stored in libs[filenum,"name"].
- # The version string of each file (if any) is stored in libs[filenum,"version"]
- # If matchLibs is true, the first & last lines of each lib are added to
- # firstLines[] and lastLines[]
- function readLibs(libFiles,firstLines,lastLines,libs,versionRequired,quiet,
- libName,regEx,b,e,matchLibs, nFiles,i,file,libNum,numLines,versionInfo) {
- nFiles = split(libFiles,files,"[ \t:]+")
- for (i = 1; i <= nFiles; i++) {
- file = files[i]
- if (file == "")
- continue
- ++libNum
- libs[libNum,"name"] = file
- numLines = readLib(file,libs,libNum SUBSEP)
- if (matchLibs) {
- firstLines[f = libs[libNum,1]]
- lastLines[l = libs[libNum,numLines]]
- if (Debug)
- printf \
- "Adding to firstLines[]: %s\nAdding to lastLines[]: %s\n",
- f,l > "/dev/stderr"
- }
- else if (\
- (!matchLines(libs[libNum,1],firstLines,regEx,"first",b,file,1) ||
- !matchLines(libs[libNum,numLines],lastLines,regEx,"last",e,file,1)))
- exit 1
- if (getLibVersion(libs,1,0,versionInfo,quiet,file,versionRequired,
- libNum SUBSEP)) {
- libs[libNum,"version"] = versionString(versionInfo)
- if (versionRequired && versionInfo["name"] != libName) {
- printf \
- "%s: name in version string of library in file %s\n"\
- " (%s) does not match name in replacement library (%s). Exiting.\n",Name,
- file,versionInfo["name"],libName > "/dev/stderr"
- exit 1
- }
- }
- }
- libs["numfiles"] = libNum
- return libNum
- }
-
- function matchLines(line,compareLines,regEx,lineName,lineGiven,file,complain,
- compareLine) {
- if (regEx) {
- for (compareLine in compareLines)
- if (line ~ compareLine)
- return 1
- }
- else if (line in compareLines) {
- if (Debug)
- printf "Found: %s\n",line > "/dev/stderr"
- return 1
- }
- if (complain)
- printf \
- "%s: %s line of comparison library file %s\n"\
- " does not match %s line %s. Exiting.\n",Name,
- lineName,file,lineName,regEx ? "pattern(s)" : (lineGiven ? \
- "comparison string(s)" : "of replacement library(s)") > "/dev/stderr"
- return 0
- }
-
- function readLib(libFile,library,prefix, libLines) {
- if (Debug)
- printf "Reading library file: \"%s\"\n",libFile > "/dev/stderr"
- if ((libLines = readFile(libFile,library,prefix)) == -1) {
- printf "%s: Error reading library file \"%s\": %s\n",Name,libFile,
- ERRNO > "/dev/stderr"
- exit 1
- }
- if (!libLines) {
- printf "%s: Library file \"%s\" is empty. Exiting.\n",Name,
- libFile > "/dev/stderr"
- exit 1
- }
- return libLines
- }
-
- function getLibMarker(lineName,startString,libFile,Options,Opt,libLine,lines,
- i) {
- if (Opt in Options) {
- lines[Options[Opt]]
- if (Debug)
- printf "Got %s-line comparison line: %s\n",lineName,
- Options[Opt] > "/dev/stderr"
- for (i = Options[Opt,"count"]; i > 1; i--) {
- lines[Options[Opt,i]]
- if (Debug)
- printf "Got %s-line comparison line: %s\n",lineName,
- Options[Opt,i] > "/dev/stderr"
- }
- }
- else if (substr(libLine,1,length(startString)) != startString) {
- printf \
- "%s: %s line of library file \"%s\"\n"\
- " does not begin with string \"%s\". Use -s or -S to override. Exiting.\n",
- Name,lineName,libFile,
- startString > "/dev/stderr"
- exit 1
- }
- else {
- if (Debug)
- printf "%s line of new library begins with \"%s\"\n",lineName,
- startString > "/dev/stderr"
- lines[libLine]
- }
- if (Debug)
- printf "Got %d %s-line comparison line(s).\n",NumElem(lines),
- lineName > "/dev/stderr"
- }
-
- # library[] is a library to search for a version string.
- # It contains lines with integer indices.
- # first is the first line number.
- # If last is non-0, it is the last line number.
- # If quiet is false, the library version is printed.
- # libFile is the file that library came from, for messages.
- # If versionRequired is true, not finding a version string is fatal.
- # If Prefix is non-null, it is used as a prefix for the indices used to
- # access library[].
- # If libname is non-null, any version string that has a name that doesn't
- # match it is ignored.
- # If a line is found, the version components are put in versionInfo[].
- # Return value:
- # Line number version string was found on if version line is found, 0 if not.
- function getLibVersion(library,first,last,versionInfo,quiet,libFile,
- versionRequired,Prefix,libname,
- i,ret) {
- ret = 0
- if (Debug)
- printf \
- "getLibVersion: searching for version on lines %d through %s; prefix is: %s\n",
- first,last ? last : "end",Prefix > "/dev/stderr"
- for (i = first; last ? (i <= last) : ((Prefix i) in library); i++) {
- if (Debug)
- printf "%d\r",i > "/dev/stderr"
- if (library[Prefix i] ~ /@\(#\)/) {
- if (Debug)
- printf "Found SCCS line in library file %s:\n%s\n",libFile,
- library[Prefix i] > "/dev/stderr"
- if (getVersion(library[Prefix i],versionInfo)) {
- if (libname != "" && versionInfo["name"] != libname) {
- if (Debug)
- printf \
- "In %s, found version string with name \"%s\";\n"\
- " doesn't match name being searched for (%s); ignoring...\n",libFile,
- versionInfo["name"],libname > "/dev/stderr"
- # Empty versionInfo[] since the existance of indexes in it
- # may be used as a test of whether a good version line was read
- split("",versionInfo)
- continue
- }
- ret = i
- break
- }
- }
- }
- if (ret) {
- if (!quiet)
- printf "Library in file %s is: %s\n",libFile,
- versionString(versionInfo) > "/dev/stderr"
- }
- else {
- if (Debug)
- printf "No version line in library in file %s\n",
- libFile > "/dev/stderr"
- if (versionRequired) {
- printf \
- "%s: No version string found in library in file \"%s\". Exiting.\n",
- Name,libFile > "/dev/stderr"
- exit 1
- }
- }
- return ret
- }
-
- # If line ends with a string of the form:
- # @( #) <string> <version-#> <date><end-of-line>
- # ^ avoid confusing programs run on this text file
- # then the string is put in versionInfo["name"], version-# is put in
- # versionInfo["ver"], and date is put in versionInfo["date"].
- # <date> must have the form num/num/num. <version-#> must consist of a
- # sequence of numeric strings separated by '.' characters.
- # If noSCCS is true, the string should contain only the name, version, and
- # date words.
- # If line does not contain a string of this form, 0 is returned and no
- # indexes are created in versionInfo; if it does, 1 is returned.
- function getVersion(line,versionInfo,noSCCS,
- pos,elem,name,version,date,nElem) {
- if (!noSCCS) {
- if (!(pos = index(line,"@(\#)")))
- return 0
- line = substr(line,pos+4)
- }
- sub("^[ \t]+","",line) # get rid of leading & trailing whitespace
- sub("[ \t]+$","",line)
- if ((nElem = split(line,elem,"[ \t]+")) != 3) {
- if (Debug)
- printf \
- "Found %d words after @(\#) on following line; wanted 3.\n%s\n",
- nElem,line > "/dev/stderr"
- return 0
- }
- name = elem[1]
- version = elem[2]
- date = elem[3]
- if (version !~ /^[0-9]+(\.[0-9]+)*$/) {
- if (Debug)
- printf \
- "Version word '%s' not of recognized format in following line:\n%s\n",
- version,line > "/dev/stderr"
- return 0
- }
- if (date !~ "^[0-9]+/[0-9]+/[0-9]+$") {
- if (Debug)
- printf \
- "Date word '%s' not of recognized format in following line:\n%s\n",
- date,line > "/dev/stderr"
- return 0
- }
- versionInfo["name"] = name
- versionInfo["ver"] = version
- versionInfo["date"] = date
- return 1
- }
-
- # v1 and v2 are sequences of integers separated by the pattern Sep.
- # Each pair of integers (from v1 and v2) is compared in turn, starting from
- # the left. If an int from v1 is < int from v2, -1 is returned.
- # If v1 and v2 compare the same, 1 is returned. If an int from v1 is > int
- # from v2, 1 is returned. If one of v1 or v2 runs out of integers before the
- # other, the one that runs out is taken to be < the one that still has
- # integers, even if the one that still has integers has a 0 as the next value.
- function compareVersion(v1,v2,Sep, v1e,v2e,i,i1,i2) {
- split(v1,v1e,Sep)
- split(v2,v2e,Sep)
- for (i = 1; i in v1e; i++)
- if (!(i in v2e)) # ran out of ints for v2, so v1 > v2
- return 1
- else {
- i1 = v1e[i]+0
- i2 = v2e[i]+0
- if (i1 > i2)
- return 1
- else if (i2 > i1)
- return -1
- }
- if (i in v2e) # ran out of ints for v1, so v2 > v1
- return -1
- return 0
- }
-
- function versionString(version) {
- return version["name"] " " version["ver"] " " version["date"]
- }
-
- # Read file into array lines, one line per element, with integer indices
- # starting at 1.
- # The index "numchars" is set to the total number of characters read.
- # The index "numlines" is set to the total number of lines read.
- # If prefix is set, all array indices start with it.
- # Return value:
- # -1 on error; the number of lines read on success.
- function readFile(file,lines,prefix, numLines,ret,numChars) {
- while ((ret = (getline < file)) == 1) {
- numLines++
- lines[prefix numLines] = $0
- numChars += length($0)+1
- }
- if (Debug)
- printf "Read %d lines from file %s. Last getline returned %d.\n",
- numLines,file,ret > "/dev/stderr"
- close(file)
- if (!ret) {
- lines[prefix "numchars"] = numChars
- lines[prefix "numlines"] = numLines
- }
- return ret ? -1 : numLines
- }
-
- function writeLines(outFile,Lines,first,last, i) {
- for (i = first; i <= last || (!last && (i in Lines)); i++)
- print Lines[i] > outFile
- return 1
- }
-
- # Compare the library in lines[] to each libary in libs[].
- # The lines of each library are in libs[libnum,1..numlines]
- # where libnum is an integer starting with 1.
- # The number of libraries is in libs["numfiles"].
- # The number of lines in each library is in libs[libnum,"numlines"]
- # The number of chars in each library is in libs[libnum,"numchars"]
- # The name of the file each library came from is in libs[libnum,"name"].
- # The version string of each library (if any) is in libs[libnum,"version"]
- # Return value: Library number if a match is found, else 0.
- # If a match is not found and addLib is true, the library being searched for
- # is added to libs[] (the version string is not added).
- function doCompareLibs(lines,libs,fileName,addLib,firstLineNum,Debug,
- lastLineNum,numChars,numLibs,numCompareLibLines,lineNum,libNum,fLineNum,
- maxIncLibLines,numLines) {
- numLibs = libs["numfiles"]
- if (firstLineNum) {
- maxIncLibLines = lines["numlines"] - firstLineNum + 1
- if (Debug)
- printf "Maximum included lib length: %d lines\n",
- maxIncLibLines > "/dev/stderr"
- }
- else {
- firstLineNum = lines["libstart"]
- lastLineNum = lines["libend"]
- numChars = lines["libnumchars"]
- numLines = lastLineNum - firstLineNum + 1
- if (Debug)
- printf \
- "Included library has %d lines, %d chars. Comparing to %d libs.\n",
- numLines, numChars, numLibs > "/dev/stderr"
- }
- for (libNum = 1; libNum <= numLibs; libNum++) {
- numCompareLibLines = libs[libNum,"numlines"]
- if (numLines) {
- if (numLines != numCompareLibLines) {
- if (Debug)
- printf "Included library is not \"%s\"; it has %d lines.\n",
- libs[libNum,"name"],numCompareLibLines > "/dev/stderr"
- continue
- }
- if (libs[libNum,"numchars"] != numChars) {
- if (Debug)
- printf "Included library is not same as \"%s\";\n"\
- " # of lines match but it has %d characters.\n",
- libs[libNum,"name"],libs[libNum,"numchars"] > "/dev/stderr"
- continue
- }
- }
- else if (numCompareLibLines > maxIncLibLines) {
- if (Debug)
- printf \
- "Included library is not \"%s\"; it has too many lines (%d).\n",
- libs[libNum,"name"],numCompareLibLines > "/dev/stderr"
- continue
- }
- fLineNum = firstLineNum
- for (lineNum = 1; lineNum <= numCompareLibLines; lineNum++) {
- if (libs[libNum,lineNum] != lines[fLineNum]) {
- if (Debug)
- printf \
- "Included library is not \"%s\"; mismatch at line %d:\n"\
- " Included lib: %s\n"\
- " Compared lib: %s\n",
- libs[libNum,"name"],lineNum,lines[fLineNum],
- libs[libNum,lineNum] > "/dev/stderr"
- break
- }
- fLineNum++
- }
- if (lineNum > numCompareLibLines) {
- if (!quiet)
- printf \
- "%s: Included library in file \"%s\" is identical to \"%s\"\n",
- Name,fileName,libs[libNum,"name"] > "/dev/stderr"
- return libNum
- }
- }
- if (addLib) {
- libs["numfiles"] = ++numLibs
- if (Debug)
- printf "Adding library from \"%s\" to library set as lib #%d...\n",
- fileName,numLibs > "/dev/stderr"
- libs[numLibs,"numchars"] = numChars
- libs[numLibs,"numlines"] = numLines
- libs[numLibs,"name"] = fileName
- fLineNum = firstLineNum
- for (lineNum = 1; lineNum <= numLines; lineNum++)
- libs[numLibs,lineNum] = lines[fLineNum++]
- }
- return 0
- }
-
- function checkByMatch(lines,inFileName,firstLines,libs,Name,Debug, libNum,
- numLines,lineNum,line) {
- numLines = lines["numlines"]
- for (lineNum = 1; lineNum <= numLines; lineNum++) {
- if (Debug)
- printf "%d\r",lineNum > "/dev/stderr"
- line = lines[lineNum]
- if (line in firstLines &&
- (libNum = doCompareLibs(lines,libs,inFileName,0,lineNum,Debug)))
- break
- }
- if (libNum) {
- lines["libstart"] = lineNum
- lines["libend"] = lineNum + libs[libNum,"numlines"] - 1
- return 1
- }
- else {
- printf "%s: None of %d comparison libraries found in file %s\n",
- Name,libs["numfiles"],inFileName > "/dev/stderr"
- return 0
- }
- }
-
- # Find a section of a file by searching for a match from among a set of first
- # and last lines or patterns.
- # Input variables:
- # lines[]: the array of lines to search. The lines have integer indexes
- # starting at 1.
- # firstLines[], lastLines[]: Sets of lines that may mark the start and end
- # of the library being searched for.
- # regEx: whether to treat the above sets as fixed lines or patterns.
- # inFileName: The name of the file, for use in messages.
- # Name: name of program, for error messages.
- # Output variables:
- # On success, lines["libstart"] and lines["libend"] are set to the first and
- # last lines of the found library, and lines["libnumchars"] is set to the
- # total number of characters in the library.
- # Return value:
- # On error (last line found before first, multiple instances of lines
- # found, or first but not last line found), -1.
- # On success, 1.
- # On failure (first and last lines not found), 0.
- function findFirstLast(lines,firstLines,lastLines,regEx,inFileName,Name,
- firstLineNum,lastLineNum,lineNum,numLines,numChars,line) {
- if (Debug)
- printf \
- "findFirstLast(): searching for any of %d start line(s), %d end line(s).\n",
- NumElem(firstLines),NumElem(lastLines) > "/dev/stderr"
- numLines = lines["numlines"]
- for (lineNum = 1; lineNum <= numLines; lineNum++) {
- if (Debug)
- printf "%d\r",lineNum > "/dev/stderr"
- line = lines[lineNum]
- # If found multiple instances of 1st line, fail.
- if (matchLines(line,firstLines,regEx)) { # if found match for 1st line
- if (!(firstLineNum = checkFoundLine(firstLineNum,lineNum,"first",
- inFileName,lines,line)))
- return -1
- }
- if (firstLineNum && !lastLineNum)
- numChars += length(line)+1
- if (matchLines(line,lastLines,regEx)) {
- if (!firstLineNum) {
- printf \
- "%s: Found match for last line of library in file\n"\
- " \"%s\" (at line %d)\n"\
- " before match for first line of library. Skipping this file.\n",
- Name,inFileName,lineNum > "/dev/stderr"
- return -1
- }
- # If found multiple instances of last line, fail.
- if (!(lastLineNum = checkFoundLine(lastLineNum,lineNum,"last",
- inFileName,lines,line)))
- return -1
- }
- }
- # Test old library
- if (!firstLineNum)
- return 0
- if (!lastLineNum) {
- printf "%s: Did not find end of library in file (skipping): %s\n",
- Name,inFileName > "/dev/stderr"
- return -1
- }
- lines["libstart"] = firstLineNum
- lines["libend"] = lastLineNum
- lines["libnumchars"] = numChars
- return 1
- }
-
- # Search a set of lines for a library. The library section is recognized by
- # its first and last lines and possibly its version string.
- # Input variables:
- # lines[]: the array of lines to search. The lines have integer indexes
- # starting at 1.
- # inFileName: The name of the file, for use in messages.
- # firstLines[], lastLines[]: Sets of lines that may mark the start and end
- # of the library being searched for.
- # regEx: whether to treat the above sets as fixed lines or patterns.
- # compareLibs: true if the lines of a found library must match one of a given
- # set of libraries.
- # libs[]: The lines of the libraries for the above comparison.
- # addLib: True if a non-matching library should be added to the library set.
- # checkVersion: If true and library section contains a version string, it
- # must be equale to or later than the version in libVersion[].
- # versionRequired: If true, library section must contain a version string.
- # getVersion: True if version information should be returned in
- # versionInfo[], but not necessarily checked.
- # libVersion[]: Version information from replacement library.
- # Name: Program name, for messages.
- # Debug: Print debugging info.
- # Output variables:
- # If version information is gotten, it is put in versionInfo[].
- # If a good library is found in lines[], the first and last line numbers of
- # the library are put in lines["libstart"] and lines["libend"], and the
- # number of characters in the library is put in lines["libnumchars"]
- # Return value:
- # 1 on success.
- # 0 if library not found.
- # -1 if a bad library was found.
- # -2 if library contents comparison fails.
- function checkByFirstLast(lines,inFileName,firstLines,lastLines,regEx,
- compareLibs,libs,addLib,Name,Debug,
- ret,numCompared) {
- ret = findFirstLast(lines,firstLines,lastLines,regEx,inFileName,Name)
- if (ret != 1)
- return ret
- if (compareLibs) {
- # Get number of current libs first, since doCompareLibs() may change it
- numCompared = libs["numfiles"]
- if (!doCompareLibs(lines,libs,inFileName,addLib,0,Debug)) {
- printf "%s: No match for library in file %s\n"\
- " found in any of %d comparison library(s).\n",Name,
- inFileName,numCompared > "/dev/stderr"
- return -2
- }
- }
- return 1
- }
-
- # Globals: versions[]
- function procFile(inFile,library,firstLines,lastLines,backup,Debug,
- checkVersion,quiet,libVersion,newExt,versionRequired,printLib,printLibVersion,
- noLib,compareLibs,compareWarning,libs,regEx,matchLibs,addLib,vName,
- uniqueVersion,
- inFileName,contentsRet,versionInfo,lines,outFile,backupFileName,vString) {
-
- inFileName = (inFile == "/dev/stdin" ? "(standard input)" : inFile)
- if (Debug)
- printf "\nProcessing file: \"%s\"\n",inFile > "/dev/stderr"
- if (readFile(inFile,lines) == -1) {
- printf "%s: Error reading file %s. Skipping.\n",Name,
- inFileName > "/dev/stderr"
- return 0
- }
- if (vName != "") {
- if (ret = getLibVersion(lines,1,0,versionInfo,1,inFileName,0,"",vName))
- {
- vString = versionInfo["ver"] " " versionInfo["date"]
- if (uniqueVersion) {
- if (vString in versions) {
- if (Debug)
- printf "Already saw version <%s>\n",
- vString > "/dev/stderr"
- return 1
- }
- else {
- if (Debug)
- printf "New version <%s>\n",vString > "/dev/stderr"
- versions[vString]
- }
- }
- printf "%s %s %d\n",inFileName,vString,ret
- return 1
- }
- else {
- if (!uniqueVersion)
- printf "%s no-version\n",inFileName
- return 0
- }
- }
- if (matchLibs)
- contentsRet = \
- checkByMatch(lines,inFileName,firstLines,libs,Name,Debug)
- else {
- contentsRet = checkByFirstLast(lines,inFileName,firstLines,lastLines,
- regEx,compareLibs,libs,addLib,Name,Debug)
- if (Debug)
- printf "checkByFirstLast() returned %d\n",
- contentsRet > "/dev/stderr"
- }
- if (!contentsRet) { # no lib found
- if (noLib)
- print inFileName
- else
- printf \
- "%s: Did not find start or end of library in file (skipping): %s\n",
- Name,inFileName > "/dev/stderr"
- return 0
- }
- if (contentsRet == -1) # bad lib
- return 0
- if (printLib || printLibVersion || checkVersion || versionRequired) {
- if (getLibVersion(lines,lines["libstart"],lines["libend"],versionInfo,
- 1,inFileName,0,"",libVersion["name"])) {
- if (checkVersion && \
- (compareLibVersions("version","\\.",versionInfo["ver"],
- libVersion["ver"],inFileName) == 1 ||
- compareLibVersions("date","/",versionInfo["date"],
- libVersion["date"],inFileName) == 1))
- return 0
- }
- else if (versionRequired) {
- printf \
- "%s: Did not find version string in library in file (skipping): %s\n",
- Name,inFileName > "/dev/stderr"
- return 0
- }
- }
- if (printLibVersion) {
- if ("name" in versionInfo)
- printf "%-12s %s\n",inFileName,versionString(versionInfo)
- else
- printf "%s: no version string.\n",inFileName > "/dev/stderr"
- return ("name" in versionInfo)
- }
- if (printLib) {
- if ("name" in versionInfo)
- printf "Library version in %s is %s\n",inFileName,
- versionString(versionInfo) > "/dev/stderr"
- # If either we are not doing a contents comparision or we are and it
- # failed, write lib contents
- if (!compareLibs || contentsRet == -2) {
- if (newExt != "")
- outFile = inFile newExt
- else
- outFile = "/dev/stdout"
- printf "Writing library from %s (%d lines) to %s\n",inFileName,
- lines["libend"] - lines["libstart"] + 1,outFile > "/dev/stderr"
- if (!writeLines(outFile,lines,lines["libstart"],lines["libend"]))
- return -1
- close(outFile)
- return 1
- }
- else
- return 0
- }
- if (noLib)
- return 1
- # If contents comparisons didn't succeed and this should be fatal, return
- if (contentsRet != 1 && !compareWarning)
- return 0
-
- # Back up old file
- if (backup && (inFile != "/dev/stdin")) {
- backupFileName = inFile "-"
- if (Debug)
- printf "Backing up \"%s\" to \"%s\"\n",inFile,
- backupFileName > "/dev/stderr"
- if (copy(inFile,backupFileName)) {
- printf "%s: Could not make backup copy of file \"%s\" in \"%s\".\n"\
- " Skipping this file.\n",Name,inFile,backupFileName > "/dev/stderr"
- return 0
- }
- }
- # Write updated file
- if (newExt != "")
- outFile = inFile newExt
- else if (inFile == "/dev/stdin")
- outFile = "/dev/stdout"
- else
- outFile = inFile
- if (Debug)
- printf \
- "Writing output (replacing lines %d through %d) to file: \"%s\"\n",
- lines["libstart"],lines["libend"],outFile > "/dev/stderr"
- if (!writeLines(outFile,lines,1,lines["libstart"]-1) ||
- !writeLines(outFile,library,1,0) ||
- !writeLines(outFile,lines,lines["libend"]+1,0)) {
- printf "File \"%s\" may be corrupted!\n",outFile > "/dev/stderr"
- if (backup)
- printf "Original file contents are in file \"%s\"\n",
- backupFileName > "/dev/stderr"
- return -1
- }
- close(outFile)
- # Print status
- if (!quiet) {
- printf "%s: Updated file \"%s\"",Name,inFileName > "/dev/stderr"
- if ("name" in versionInfo)
- printf " (had library %s)",
- versionString(versionInfo) > "/dev/stderr"
- print "" > "/dev/stderr"
- }
- return 1
- }
-
- # Compare version element in oldVersion to that in libVersion.
- # See compareVersion() for a description of the format of the version strings
- # and Sep.
- # verName is the version component being compared.
- # inFile is the name of the file that oldVersion came from.
- # Return value:
- # -1 if oldVersion is earlier/lower than libVersion
- # 0 if oldVersion is equale to libVersion
- # 1 if oldVersion is later/higher than libVersion
- function compareLibVersions(verName,Sep,oldVersion,libVersion,inFile, rel) {
- rel = compareVersion(oldVersion,libVersion,Sep)
- if (rel == 1) {
- printf "%s: Library %s in file \"%s\" (%s)\n"\
- " appears to be later than %s of new library %s (%s).\n"\
- " Skipping this file.\n",
- Name,verName,inFile,oldVersion,verName,verName,
- libVersion > "/dev/stderr"
- }
- else if (!rel)
- printf "%s: Note: Library %s in file \"%s\" (%s)\n"\
- " appears to be the same as %s of new library %s (%s). Continuing.\n",
- Name,verName,inFile,oldVersion,verName,verName,
- libVersion > "/dev/stderr"
- if (rel == 1 && Debug)
- printf "File \"%s\": Old library %s was %s.\n",inFile,libVersion,
- verName > "/dev/stderr"
- return rel
- }
-
- function checkFoundLine(oldLineNum,lineNum,lineName,inFileName,lines,
- line2)
- {
- if (oldLineNum) {
- printf \
- "%s: Found multiple instances of %s line of library in file\n"\
- " \"%s\". Skipping this file.\n"\
- " 1st (line %d): %s\n"\
- " 2nd (line %d): %s\n",
- Name,lineName,inFileName,oldLineNum,
- lines[oldLineNum],lineNum,line2 > "/dev/stderr"
- return 0
- }
- else {
- if (Debug)
- printf "File \"%s\": Found %s line of library at line %d.\n",
- inFileName,lineName,lineNum > "/dev/stderr"
- return lineNum
- }
- }
-
- ### Begin copy,append routines
- # append: append file Source to file Dest.
- # The final return value from the read is returned.
- # It will be 0 if the file was read successfully; -1 if not.
- function append(Source,Dest, Line,ret) {
- while ((ret = (getline Line < Source)) == 1)
- print Line >> Dest
- close(Source)
- close(Dest)
- return ret
- }
-
- # copy: append file Source to file Dest.
- # The final return value from the read is returned.
- # It will be 0 if the file was read successfully; -1 if not.
- function copy(Source,Dest, Line,ret) {
- while ((ret = (getline Line < Source)) == 1)
- print Line > Dest
- close(Source)
- close(Dest)
- return ret
- }
-
- ### End copy,append routines
- ### Start of ProcArgs library
- # @(#) ProcArgs 1.11 96/12/08
- # 92/02/29 john h. dubois iii (john@armory.com)
- # 93/07/18 Added "#" arg type
- # 93/09/26 Do not count -h against MinArgs
- # 94/01/01 Stop scanning at first non-option arg. Added ">" option type.
- # Removed meaning of "+" or "-" by itself.
- # 94/03/08 Added & option and *()< option types.
- # 94/04/02 Added NoRCopt to Opts()
- # 94/06/11 Mark numeric variables as such.
- # 94/07/08 Opts(): Do not require any args if h option is given.
- # 95/01/22 Record options given more than once. Record option num in argv.
- # 95/06/08 Added ExclusiveOptions().
- # 96/01/20 Let rcfiles be a colon-separated list of filenames.
- # Expand $VARNAME at the start of its filenames.
- # Let varname=0 and -option- turn off an option.
- # 96/05/05 Changed meaning of 7th arg to Opts; now can specify exactly how many
- # of the vars should be searched for in the environment.
- # Check for duplicate rcfiles.
- # 96/05/13 Return more specific error values. Note: ProcArgs() and InitOpts()
- # now return various negatives values on error, not just -1, and
- # Opts() may set Err to various positive values, not just 1.
- # Added AllowUnrecOpt.
- # 96/05/23 Check type given for & option
- # 96/06/15 Re-port to awk
- # 96/10/01 Moved file-reading code into ReadConfFile(), so that it can be
- # used by other functions.
- # 96/10/15 Added OptChars
- # 96/11/01 Added exOpts arg to Opts()
- # 96/11/16 Added ; type
- # 96/12/08 Added Opt2Set() & Opt2Sets()
- # 96/12/27 Added CmdLineOpt()
-
- # optlist is a string which contains all of the possible command line options.
- # A character followed by certain characters indicates that the option takes
- # an argument, with type as follows:
- # : String argument
- # ; Non-empty string argument
- # * Floating point argument
- # ( Non-negative floating point argument
- # ) Positive floating point argument
- # # Integer argument
- # < Non-negative integer argument
- # > Positive integer argument
- # The only difference the type of argument makes is in the runtime argument
- # error checking that is done.
-
- # The & option is a special case used to get numeric options without the
- # user having to give an option character. It is shorthand for [-+.0-9].
- # If & is included in optlist and an option string that begins with one of
- # these characters is seen, the value given to "&" will include the first
- # char of the option. & must be followed by a type character other than ":"
- # or ";".
- # Note that if e.g. &> is given, an option of -.5 will produce an error.
-
- # Strings in argv[] which begin with "-" or "+" are taken to be
- # strings of options, except that a string which consists solely of "-"
- # or "+" is taken to be a non-option string; like other non-option strings,
- # it stops the scanning of argv and is left in argv[].
- # An argument of "--" or "++" also stops the scanning of argv[] but is removed.
- # If an option takes an argument, the argument may either immediately
- # follow it or be given separately.
- # "-" and "+" options are treated the same. "+" is allowed because most awks
- # take any -options to be arguments to themselves. gawk 2.15 was enhanced to
- # stop scanning when it encounters an unrecognized option, though until 2.15.5
- # this feature had a flaw that caused problems in some cases. See the OptChars
- # parameter to explicitly set the option-specifier characters.
-
- # If an option that does not take an argument is given,
- # an index with its name is created in Options and its value is set to the
- # number of times it occurs in argv[].
-
- # If an option that does take an argument is given, an index with its name is
- # created in Options and its value is set to the value of the argument given
- # for it, and Options[option-name,"count"] is (initially) set to the 1.
- # If an option that takes an argument is given more than once,
- # Options[option-name,"count"] is incremented, and the value is assigned to
- # the index (option-name,instance) where instance is 2 for the second occurance
- # of the option, etc.
- # In other words, the first time an option with a value is encountered, the
- # value is assigned to an index consisting only of its name; for any further
- # occurances of the option, the value index has an extra (count) dimension.
-
- # The sequence number for each option found in argv[] is stored in
- # Options[option-name,"num",instance], where instance is 1 for the first
- # occurance of the option, etc. The sequence number starts at 1 and is
- # incremented for each option, both those that have a value and those that
- # do not. Options set from a config file have a value of 0 assigned to this.
-
- # Options and their arguments are deleted from argv.
- # Note that this means that there may be gaps left in the indices of argv[].
- # If compress is nonzero, argv[] is packed by moving its elements so that
- # they have contiguous integer indices starting with 0.
- # Option processing will stop with the first unrecognized option, just as
- # though -- was given except that unlike -- the unrecognized option will not be
- # removed from ARGV[]. Normally, an error value is returned in this case.
- # If AllowUnrecOpt is true, it is not an error for an unrecognized option to
- # be found, so the number of remaining arguments is returned instead.
- # If OptChars is not a null string, it is the set of characters that indicate
- # that an argument is an option string if the string begins with one of the
- # characters. A string consisting solely of two of the same option-indicator
- # characters stops the scanning of argv[]. The default is "-+".
- # argv[0] is not examined.
- # The number of arguments left in argc is returned.
- # If an error occurs, the global string OptErr is set to an error message
- # and a negative value is returned.
- # Current error values:
- # -1: option that required an argument did not get it.
- # -2: argument of incorrect type supplied for an option.
- # -3: unrecognized (invalid) option.
- function ProcArgs(argc,argv,OptList,Options,compress,AllowUnrecOpt,OptChars,
- ArgNum,ArgsLeft,Arg,ArgLen,ArgInd,Option,Pos,NumOpt,Value,HadValue,specGiven,
- NeedNextOpt,GotValue,OptionNum,Escape,dest,src,count,c,OptTerm,OptCharSet)
- {
- # ArgNum is the index of the argument being processed.
- # ArgsLeft is the number of arguments left in argv.
- # Arg is the argument being processed.
- # ArgLen is the length of the argument being processed.
- # ArgInd is the position of the character in Arg being processed.
- # Option is the character in Arg being processed.
- # Pos is the position in OptList of the option being processed.
- # NumOpt is true if a numeric option may be given.
- ArgsLeft = argc
- NumOpt = index(OptList,"&")
- OptionNum = 0
- if (OptChars == "")
- OptChars = "-+"
- while (OptChars != "") {
- c = substr(OptChars,1,1)
- OptChars = substr(OptChars,2)
- OptCharSet[c]
- OptTerm[c c]
- }
- for (ArgNum = 1; ArgNum < argc; ArgNum++) {
- Arg = argv[ArgNum]
- if (length(Arg) < 2 || !((specGiven = substr(Arg,1,1)) in OptCharSet))
- break # Not an option; quit
- if (Arg in OptTerm) {
- delete argv[ArgNum]
- ArgsLeft--
- break
- }
- ArgLen = length(Arg)
- for (ArgInd = 2; ArgInd <= ArgLen; ArgInd++) {
- Option = substr(Arg,ArgInd,1)
- if (NumOpt && Option ~ /[-+.0-9]/) {
- # If this option is a numeric option, make its flag be & and
- # its option string flag position be the position of & in
- # the option string.
- Option = "&"
- Pos = NumOpt
- # Prefix Arg with a char so that ArgInd will point to the
- # first char of the numeric option.
- Arg = "&" Arg
- ArgLen++
- }
- # Find position of flag in option string, to get its type (if any).
- # Disallow & as literal flag.
- else if (!(Pos = index(OptList,Option)) || Option == "&") {
- if (AllowUnrecOpt) {
- Escape = 1
- break
- }
- else {
- OptErr = "Invalid option: " specGiven Option
- return -3
- }
- }
-
- # Find what the value of the option will be if it takes one.
- # NeedNextOpt is true if the option specifier is the last char of
- # this arg, which means that if the option requires a value it is
- # the next arg.
- if (NeedNextOpt = (ArgInd >= ArgLen)) { # Value is the next arg
- if (GotValue = ArgNum + 1 < argc)
- Value = argv[ArgNum+1]
- }
- else { # Value is included with option
- Value = substr(Arg,ArgInd + 1)
- GotValue = 1
- }
-
- if (HadValue = AssignVal(Option,Value,Options,
- substr(OptList,Pos + 1,1),GotValue,"",++OptionNum,!NeedNextOpt,
- specGiven)) {
- if (HadValue < 0) # error occured
- return HadValue
- if (HadValue == 2)
- ArgInd++ # Account for the single-char value we used.
- else {
- if (NeedNextOpt) { # option took next arg as value
- delete argv[++ArgNum]
- ArgsLeft--
- }
- break # This option has been used up
- }
- }
- }
- if (Escape)
- break
- # Do not delete arg until after processing of it, so that if it is not
- # recognized it can be left in ARGV[].
- delete argv[ArgNum]
- ArgsLeft--
- }
- if (compress != 0) {
- dest = 1
- src = argc - ArgsLeft + 1
- for (count = ArgsLeft - 1; count; count--) {
- ARGV[dest] = ARGV[src]
- dest++
- src++
- }
- }
- return ArgsLeft
- }
-
- # Assignment to values in Options[] occurs only in this function.
- # Option: Option specifier character.
- # Value: Value to be assigned to option, if it takes a value.
- # Options[]: Options array to return values in.
- # ArgType: Argument type specifier character.
- # GotValue: Whether any value is available to be assigned to this option.
- # Name: Name of option being processed.
- # OptionNum: Number of this option (starting with 1) if set in argv[],
- # or 0 if it was given in a config file or in the environment.
- # SingleOpt: true if the value (if any) that is available for this option was
- # given as part of the same command line arg as the option. Used only for
- # options from the command line.
- # specGiven is the option specifier character use, if any (e.g. - or +),
- # for use in error messages.
- # Global variables: OptErr
- # Return value: negative value on error, 0 if option did not require an
- # argument, 1 if it did & used the whole arg, 2 if it required just one char of
- # the arg.
- # Current error values:
- # -1: Option that required an argument did not get it.
- # -2: Value of incorrect type supplied for option.
- # -3: Bad type given for option &
- function AssignVal(Option,Value,Options,ArgType,GotValue,Name,OptionNum,
- SingleOpt,specGiven, UsedValue,Err,NumTypes) {
- # If option takes a value... [
- NumTypes = "*()#<>]"
- if (Option == "&" && ArgType !~ "[" NumTypes) { # ]
- OptErr = "Bad type given for & option"
- return -3
- }
-
- if (UsedValue = (ArgType ~ "[:;" NumTypes)) { # ]
- if (!GotValue) {
- if (Name != "")
- OptErr = "Variable requires a value -- " Name
- else
- OptErr = "option requires an argument -- " Option
- return -1
- }
- if ((Err = CheckType(ArgType,Value,Option,Name,specGiven)) != "") {
- OptErr = Err
- return -2
- }
- # Mark this as a numeric variable; will be propogated to Options[] val.
- if (ArgType != ":" && ArgType != ";")
- Value += 0
- if ((Instance = ++Options[Option,"count"]) > 1)
- Options[Option,Instance] = Value
- else
- Options[Option] = Value
- }
- # If this is an environ or rcfile assignment & it was given a value...
- else if (!OptionNum && Value != "") {
- UsedValue = 1
- # If the value is "0" or "-" and this is the first instance of it,
- # do not set Options[Option]; this allows an assignment in an rcfile to
- # turn off an option (for the simple "Option in Options" test) in such
- # a way that it cannot be turned on in a later file.
- if (!(Option in Options) && (Value == "0" || Value == "-"))
- Instance = 1
- else
- Instance = ++Options[Option]
- # Save the value even though this is a flag
- Options[Option,Instance] = Value
- }
- # If this is a command line flag and has a - following it in the same arg,
- # it is being turned off.
- else if (OptionNum && SingleOpt && substr(Value,1,1) == "-") {
- UsedValue = 2
- if (Option in Options)
- Instance = ++Options[Option]
- else
- Instance = 1
- Options[Option,Instance]
- }
- # If this is a flag assignment without a value, increment the count for the
- # flag unless it was turned off. The indicator for a flag being turned off
- # is that the flag index has not been set in Options[] but it has an
- # instance count.
- else if (Option in Options || !((Option,1) in Options))
- # Increment number of times this flag seen; will inc null value to 1
- Instance = ++Options[Option]
- Options[Option,"num",Instance] = OptionNum
- return UsedValue
- }
-
- # Option is the option letter
- # Value is the value being assigned
- # Name is the var name of the option, if any
- # ArgType is one of:
- # : String argument
- # ; Non-null string argument
- # * Floating point argument
- # ( Non-negative floating point argument
- # ) Positive floating point argument
- # # Integer argument
- # < Non-negative integer argument
- # > Positive integer argument
- # specGiven is the option specifier character use, if any (e.g. - or +),
- # for use in error messages.
- # Returns null on success, err string on error
- function CheckType(ArgType,Value,Option,Name,specGiven, Err,ErrStr) {
- if (ArgType == ":")
- return ""
- if (ArgType == ";") {
- if (Value == "")
- Err = "must be a non-empty string"
- }
- # A number begins with optional + or -, and is followed by a string of
- # digits or a decimal with digits before it, after it, or both
- else if (Value !~ /^[-+]?([0-9]+|[0-9]*\.[0-9]+|[0-9]+\.)$/)
- Err = "must be a number"
- else if (ArgType ~ "[#<>]" && Value ~ /\./)
- Err = "may not include a fraction"
- else if (ArgType ~ "[()<>]" && Value < 0)
- Err = "may not be negative"
- # (
- else if (ArgType ~ "[)>]" && Value == 0)
- Err = "must be a positive number"
- if (Err != "") {
- ErrStr = "Bad value \"" Value "\". Value assigned to "
- if (Name != "")
- return ErrStr "variable " substr(Name,1,1) " " Err
- else {
- if (Option == "&")
- Option = Value
- return ErrStr "option " specGiven substr(Option,1,1) " " Err
- }
- }
- else
- return ""
- }
-
- # Note: only the above functions are needed by ProcArgs.
- # The rest of these functions call ProcArgs() and also do other
- # option-processing stuff.
-
- # Opts: Process command line arguments.
- # Opts processes command line arguments using ProcArgs()
- # and checks for errors. If an error occurs, a message is printed
- # and the program is exited.
- #
- # Input variables:
- # Name is the name of the program, for error messages.
- # Usage is a usage message, for error messages.
- # OptList the option description string, as used by ProcArgs().
- # MinArgs is the minimum number of non-option arguments that this
- # program should have, non including ARGV[0] and +h.
- # If the program does not require any non-option arguments,
- # MinArgs should be omitted or given as 0.
- # rcFiles, if given, is a colon-seprated list of filenames to read for
- # variable initialization. If a filename begins with ~/, the ~ is replaced
- # by the value of the environment variable HOME. If a filename begins with
- # $, the part from the character after the $ up until (but not including)
- # the first character not in [a-zA-Z0-9_] will be searched for in the
- # environment; if found its value will be substituted, if not the filename will
- # be discarded.
- # rcfiles are read in the order given.
- # Values given in them will not override values given on the command line,
- # and values given in later files will not override those set in earlier
- # files, because AssignVal() will store each with a different instance index.
- # The first instance of each variable, either on the command line or in an
- # rcfile, will be stored with no instance index, and this is the value
- # normally used by programs that call this function.
- # VarNames is a comma-separated list of variable names to map to options,
- # in the same order as the options are given in OptList.
- # If EnvSearch is given and nonzero, the first EnvSearch variables will also be
- # searched for in the environment. If set to -1, all values will be searched
- # for in the environment. Values given in the environment will override
- # those given in the rcfiles but not those given on the command line.
- # NoRCopt, if given, is an additional letter option that if given on the
- # command line prevents the rcfiles from being read.
- # See ProcArgs() for a description of AllowUnRecOpt and optChars, and
- # ExclusiveOptions() for a description of exOpts.
- # Special options:
- # If x is made an option and is given, some debugging info is output.
- # h is assumed to be the help option.
-
- # Global variables:
- # The command line arguments are taken from ARGV[].
- # The arguments that are option specifiers and values are removed from
- # ARGV[], leaving only ARGV[0] and the non-option arguments.
- # The number of elements in ARGV[] should be in ARGC.
- # After processing, ARGC is set to the number of elements left in ARGV[].
- # The option values are put in Options[].
- # On error, Err is set to a positive integer value so it can be checked for in
- # an END block.
- # Return value: The number of elements left in ARGV is returned.
- # Must keep OptErr global since it may be set by InitOpts().
- function Opts(Name,Usage,OptList,MinArgs,rcFiles,VarNames,EnvSearch,NoRCopt,
- AllowUnrecOpt,optChars,exOpts, ArgsLeft,e) {
- if (MinArgs == "")
- MinArgs = 0
- ArgsLeft = ProcArgs(ARGC,ARGV,OptList NoRCopt,Options,1,AllowUnrecOpt,
- optChars)
- if (ArgsLeft < (MinArgs+1) && !("h" in Options)) {
- if (ArgsLeft >= 0) {
- OptErr = "Not enough arguments"
- Err = 4
- }
- else
- Err = -ArgsLeft
- printf "%s: %s.\nUse -h for help.\n%s\n",
- Name,OptErr,Usage > "/dev/stderr"
- exit 1
- }
- if (rcFiles != "" && (NoRCopt == "" || !(NoRCopt in Options)) &&
- (e = InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch)) < 0)
- {
- print Name ": " OptErr ".\nUse -h for help." > "/dev/stderr"
- Err = -e
- exit 1
- }
- if ((exOpts != "") && ((OptErr = ExclusiveOptions(exOpts,Options)) != ""))
- {
- printf "%s: Error: %s\n",Name,OptErr > "/dev/stderr"
- Err = 1
- exit 1
- }
- return ArgsLeft
- }
-
- # ReadConfFile(): Read a file containing var/value assignments, in the form
- # <variable-name><assignment-char><value>.
- # Whitespace (spaces and tabs) around a variable (leading whitespace on the
- # line and whitespace between the variable name and the assignment character)
- # is stripped. Lines that do not contain an assignment operator or which
- # contain a null variable name are ignored, other than possibly being noted in
- # the return value. If more than one assignment is made to a variable, the
- # first assignment is used.
- # Input variables:
- # File is the file to read.
- # Comment is the line-comment character. If it is found as the first non-
- # whitespace character on a line, the line is ignored.
- # Assign is the assignment string. The first instance of Assign on a line
- # separates the variable name from its value.
- # If StripWhite is true, whitespace around the value (whitespace between the
- # assignment char and trailing whitespace on the line) is stripped.
- # VarPat is a pattern that variable names must match.
- # Example: "^[a-zA-Z][a-zA-Z0-9]+$"
- # If FlagsOK is true, variables are allowed to be "set" by being put alone on
- # a line; no assignment operator is needed. These variables are set in
- # the output array with a null value. Lines containing nothing but
- # whitespace are still ignored.
- # Output variables:
- # Values[] contains the assignments, with the indexes being the variable names
- # and the values being the assigned values.
- # Lines[] contains the line number that each variable occured on. A flag set
- # is record by giving it an index in Lines[] but not in Values[].
- # Return value:
- # If any errors occur, a string consisting of descriptions of the errors
- # separated by newlines is returned. In no case will the string start with a
- # numeric value. If no errors occur, the number of lines read is returned.
- function ReadConfigFile(Values,Lines,File,Comment,Assign,StripWhite,VarPat,
- FlagsOK,
- Line,Status,Errs,AssignLen,LineNum,Var,Val) {
- if (Comment != "")
- Comment = "^" Comment
- AssignLen = length(Assign)
- if (VarPat == "")
- VarPat = "." # null varname not allowed
- while ((Status = (getline Line < File)) == 1) {
- LineNum++
- sub("^[ \t]+","",Line)
- if (Line == "") # blank line
- continue
- if (Comment != "" && Line ~ Comment)
- continue
- if (Pos = index(Line,Assign)) {
- Var = substr(Line,1,Pos-1)
- Val = substr(Line,Pos+AssignLen)
- if (StripWhite) {
- sub("^[ \t]+","",Val)
- sub("[ \t]+$","",Val)
- }
- }
- else {
- Var = Line # If no value, var is entire line
- Val = ""
- }
- if (!FlagsOK && Val == "") {
- Errs = Errs \
- sprintf("\nBad assignment on line %d of file %s: %s",
- LineNum,File,Line)
- continue
- }
- sub("[ \t]+$","",Var)
- if (Var !~ VarPat) {
- Errs = Errs sprintf("\nBad variable name on line %d of file %s: %s",
- LineNum,File,Var)
- continue
- }
- if (!(Var in Lines)) {
- Lines[Var] = LineNum
- if (Pos)
- Values[Var] = Val
- }
- }
- if (Status)
- Errs = Errs "\nCould not read file " File
- close(File)
- return Errs == "" ? LineNum : substr(Errs,2) # Skip first newline
- }
-
- # Variables:
- # Data is stored in Options[].
- # rcFiles, OptList, VarNames, and EnvSearch are as as described for Opts().
- # Global vars:
- # Sets OptErr. Uses ENVIRON[].
- # If anything is read from any of the rcfiles, sets READ_RCFILE to 1.
- function InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch,
- Line,Var,Pos,Vars,Map,CharOpt,NumVars,TypesInd,Types,Type,Ret,i,rcFile,
- fNames,numrcFiles,filesRead,Err,Values,retStr) {
- split("",filesRead,"") # make awk know this is an array
- NumVars = split(VarNames,Vars,",")
- TypesInd = Ret = 0
- if (EnvSearch == -1)
- EnvSearch = NumVars
- for (i = 1; i <= NumVars; i++) {
- Var = Vars[i]
- CharOpt = substr(OptList,++TypesInd,1)
- if (CharOpt ~ "^[:;*()#<>&]$")
- CharOpt = substr(OptList,++TypesInd,1)
- Map[Var] = CharOpt
- Types[Var] = Type = substr(OptList,TypesInd+1,1)
- # Do not overwrite entries from environment
- if (i <= EnvSearch && Var in ENVIRON &&
- (Err = AssignVal(CharOpt,ENVIRON[Var],Options,Type,1,Var,0)) < 0)
- return Err
- }
-
- numrcFiles = split(rcFiles,fNames,":")
- for (i = 1; i <= numrcFiles; i++) {
- rcFile = fNames[i]
- if (rcFile ~ "^~/")
- rcFile = ENVIRON["HOME"] substr(rcFile,2)
- else if (rcFile ~ /^\$/) {
- rcFile = substr(rcFile,2)
- match(rcFile,"^[a-zA-Z0-9_]*")
- envvar = substr(rcFile,1,RLENGTH)
- if (envvar in ENVIRON)
- rcFile = ENVIRON[envvar] substr(rcFile,RLENGTH+1)
- else
- continue
- }
- if (rcFile in filesRead)
- continue
- # rcfiles are liable to be given more than once, e.g. UHOME and HOME
- # may be the same
- filesRead[rcFile]
- if ("x" in Options)
- printf "Reading configuration file %s\n",rcFile > "/dev/stderr"
- retStr = ReadConfigFile(Values,Lines,rcFile,"#","=",0,"",1)
- if (retStr > 0)
- READ_RCFILE = 1
- else if (ret != "") {
- OptErr = retStr
- Ret = -1
- }
- for (Var in Lines)
- if (Var in Map) {
- if ((Err = AssignVal(Map[Var],
- Var in Values ? Values[Var] : "",Options,Types[Var],
- Var in Values,Var,0)) < 0)
- return Err
- }
- else {
- OptErr = sprintf(\
- "Unknown var \"%s\" assigned to on line %d\nof file %s",Var,
- Lines[Var],rcFile)
- Ret = -1
- }
- }
-
- if ("x" in Options)
- for (Var in Map)
- if (Map[Var] in Options)
- printf "(%s) %s=%s\n",Map[Var],Var,Options[Map[Var]] > \
- "/dev/stderr"
- else
- printf "(%s) %s not set\n",Map[Var],Var > "/dev/stderr"
- return Ret
- }
-
- # OptSets is a semicolon-separated list of sets of option sets.
- # Within a list of option sets, the option sets are separated by commas. For
- # each set of sets, if any option in one of the sets is in Options[] AND any
- # option in one of the other sets is in Options[], an error string is returned.
- # If no conflicts are found, nothing is returned.
- # Example: if OptSets = "ab,def,g;i,j", an error will be returned due to
- # the exclusions presented by the first set of sets (ab,def,g) if:
- # (a or b is in Options[]) AND (d, e, or f is in Options[]) OR
- # (a or b is in Options[]) AND (g is in Options) OR
- # (d, e, or f is in Options[]) AND (g is in Options)
- # An error will be returned due to the exclusions presented by the second set
- # of sets (i,j) if: (i is in Options[]) AND (j is in Options[]).
- # todo: make options given on command line unset options given in config file
- # todo: that they conflict with.
- function ExclusiveOptions(OptSets,Options,
- Sets,SetSet,NumSets,Pos1,Pos2,Len,s1,s2,c1,c2,ErrStr,L1,L2,SetSets,NumSetSets,
- SetNum,OSetNum) {
- NumSetSets = split(OptSets,SetSets,";")
- # For each set of sets...
- for (SetSet = 1; SetSet <= NumSetSets; SetSet++) {
- # NumSets is the number of sets in this set of sets.
- NumSets = split(SetSets[SetSet],Sets,",")
- # For each set in a set of sets except the last...
- for (SetNum = 1; SetNum < NumSets; SetNum++) {
- s1 = Sets[SetNum]
- L1 = length(s1)
- for (Pos1 = 1; Pos1 <= L1; Pos1++)
- # If any of the options in this set was given, check whether
- # any of the options in the other sets was given. Only check
- # later sets since earlier sets will have already been checked
- # against this set.
- if ((c1 = substr(s1,Pos1,1)) in Options)
- for (OSetNum = SetNum+1; OSetNum <= NumSets; OSetNum++) {
- s2 = Sets[OSetNum]
- L2 = length(s2)
- for (Pos2 = 1; Pos2 <= L2; Pos2++)
- if ((c2 = substr(s2,Pos2,1)) in Options)
- ErrStr = ErrStr "\n"\
- sprintf("Cannot give both %s and %s options.",
- c1,c2)
- }
- }
- }
- if (ErrStr != "")
- return substr(ErrStr,2)
- return ""
- }
-
- # The value of each instance of option Opt that occurs in Options[] is made an
- # index of Set[].
- # The return value is the number of instances of Opt in Options.
- function Opt2Set(Options,Opt,Set, count) {
- if (!(Opt in Options))
- return 0
- Set[Options[Opt]]
- count = Options[Opt,"count"]
- for (; count > 1; count--)
- Set[Options[Opt,count]]
- return count
- }
-
- # The value of each instance of option Opt that occurs in Options[] that
- # begins with "!" is made an index of nSet[] (with the ! stripped from it).
- # Other values are made indexes of Set[].
- # The return value is the number of instances of Opt in Options.
- function Opt2Sets(Options,Opt,Set,nSet, count,aSet,ret) {
- ret = Opt2Set(Options,Opt,aSet)
- for (value in aSet)
- if (substr(value,1,1) == "!")
- nSet[substr(value,2)]
- else
- Set[value]
- return ret
- }
-
- # Returns true if option Opt was given on the command line.
- function CmdLineOpt(Options,Opt, i) {
- for (i = 1; (Opt,"num",i) in Options; i++)
- if (Options[Opt,"num",i] != 0)
- return 1
- return 0
- }
- ### End of ProcArgs library
- ### Begin array routines
-
- # InitArr: Initialize an array with values.
- # Ind and Vals are separated into lists on Sep.
- # For each item in Ind, an index with that name is created in Arr[],
- # and the value with the same position in Vals is stored in it.
- # Global variables: none.
- function InitArr(Arr,Ind,Vals,sep, numind,indnames,values) {
- split(Ind,indnames,sep)
- split(Vals,values,sep)
- for (numind in indnames)
- Arr[indnames[numind]] = values[numind]
- }
-
- function ClearArr(Arr, Elem) {
- for (Elem in Arr)
- delete Arr[Elem]
- }
-
- function CopyArr(From,To, Elem) {
- for (Elem in From)
- To[Elem] = From[Elem]
- }
-
- # Subtract the values in Subtrahend from those in Minuend
- function SubtractArr(Minuend,Subtrahend, Elem) {
- for (Elem in Subtrahend)
- Minuend[Elem] -= Subtrahend[Elem]
- }
- # For each element of the array In, an element is created in Out having
- # an index equal to the value of the element in In and a value equal to
- # the index of the element in In.
- function Invert(In,Out, Index) {
- for (Index in In)
- Out[In[Index]] = Index
- }
-
- # Assign: make an array from a list of assignments.
- # An index with the name of each variable in the list is created in the array.
- # Its value is set to the value given for it.
- # Input variables:
- # Elements is a string containing the list of variable-value pairs.
- # Sep is the string that separates the pairs in the list.
- # AssignOp is the string that separates variables from values.
- # Output variables:
- # Arr is the array.
- # Return value: the number of elements added to the set.
- # Example:
- # Assign(Arr,"foo=blot bar=blat baz=blit"," ","=")
- function Assign(Arr,Elements,Sep,AssignOp,
- Num,Names,Elem,Assignments,Assignment,i) {
- Num = split(Elements,Assignments,Sep)
- for (i = 1; i <= Num; i++) {
- Assignment = Assignments[i]
- Ind = index(Assignment,AssignOp)
- Arr[substr(Assignment,1,Ind - 1)] = substr(Assignment,Ind + 1)
- }
- return Num
- }
-
- # Packs Arr[], which should have integer indices starting at or above n, to
- # contiguous integer indices starting with n.
- # If n is not given it defaults to 0.
- # Num should be the number of elements in Arr.
- function PackArr(Arr,Num,n, NewInd,OldInd) {
- NewInd = OldInd = n+0
- for (; Num; Num--) {
- while (!(OldInd in Arr))
- OldInd++
- if (NewInd != OldInd) {
- Arr[NewInd] = Arr[OldInd]
- delete Arr[OldInd]
- }
- OldInd++
- NewInd++
- }
- }
- ### End array routines
- ### Begin set library
- # 96/05/23 added return values jhdiii
- # 96/05/25 added set2list()
-
- # Return value: the number of new elements added to Inter
- function Intersection(A,B,Inter, Elem,Count) {
- for (Elem in A)
- if (Elem in B && !(Elem in Inter)) {
- Inter[Elem]
- Count++
- }
- return Count
- }
-
- # Return value: the number of new elements added to Both
- function Union(A,B,Both) {
- return CopySet(A,Both) + CopySet(B,Both)
- }
-
- # Deletes any elements that are in both Minuend and Subtrahend from Minuend.
- # Return value: the number of elements deleted.
- function SubtractSet(Minuend,Subtrahend, Elem,nDel) {
- for (Elem in Subtrahend)
- if (Elem in Minuend) {
- delete Minuend[Elem]
- nDel++
- }
- return nDel
- }
-
- # Return value: the number of new elements added to To
- function CopySet(From,To, Elem,n) {
- for (Elem in From)
- if (!(Elem in To)) {
- To[Elem]
- n++
- }
- return n
- }
-
- # Returns 1 if Set is empty, 0 if not.
- function IsEmpty(Set, i) {
- for (i in Set)
- return 0
- return 1
- }
-
- # MakeSet: make a set from a list.
- # An index with the name of each element of the list is created in the given
- # array.
- # Input variables:
- # Elements is a string containing the list of elements.
- # Sep is the character that separates the elements of the list.
- # Output variables:
- # Set is the array.
- # Return value: the number of new elements added to the set.
- function MakeSet(Set,Elements,Sep, i,Num,Names,nFound,ind) {
- nFound = 0
- Num = split(Elements,Names,Sep)
- for (i = 1; i <= Num; i++) {
- ind = Names[i]
- if (!(ind in Set)) {
- Set[ind]
- nFound++
- }
- }
- return nFound
- }
-
- # Returns the number of elements in set Set
- function NumElem(Set, elem,Num) {
- for (elem in Set)
- Num++
- return Num
- }
-
- # Remove all elements from Set
- function DeleteAll(Set, i) {
- split("",Set,",")
- }
-
- # Returns a list of all of the elements in Set[], with each pair of elements
- # separated by Sep.
- function set2list(Set,Sep, list,elem) {
- for (elem in Set)
- list = list Sep elem
- return substr(list,2) # skip 1st separator
- }
- ### End set library
-